home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / smail-3.1.28 / src / addr.c next >
Encoding:
C/C++ Source or Header  |  1992-08-02  |  37.8 KB  |  1,537 lines

  1. /* @(#)src/addr.c    1.10 8/3/92 04:45:11 */
  2.  
  3. /*
  4.  *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  5.  *    Copyright (C) 1992  Ronald S. Karr
  6.  * 
  7.  * See the file COPYING, distributed with smail, for restriction
  8.  * and warranty information.
  9.  */
  10.  
  11. /*
  12.  * addr.c:
  13.  *    routines to parse addresses
  14.  *
  15.  *    external functions:  preparse_address, parse_address,
  16.  *                 build_uucp_route, build_partial_uucp_route,
  17.  *                 address_token, back_address_token, alloc_addr,
  18.  *                 insert_addr_list, addr_sort, split_addr_list
  19.  */
  20. #include <stdio.h>
  21. #include <ctype.h>
  22. #include "defs.h"
  23. #include "smail.h"
  24. #include "addr.h"
  25. #include "dys.h"
  26. #include "exitcodes.h"
  27. #ifndef DEPEND
  28. # include "debug.h"
  29. # include "extern.h"
  30. #endif
  31.  
  32. /* functions local to this file */
  33. static int check_target_and_remainder();
  34. static char *escaped();
  35. static char *internal_build_uucp_route();
  36. static int addrcmp();
  37.  
  38.  
  39. /*
  40.  * preparse_address - do preliminary parsing that might be needed for address
  41.  *
  42.  * this routine should be used when an address is first extracted from a
  43.  * source.  It transforms some mutant addressing forms into something more
  44.  * managable.
  45.  *
  46.  * Transformations:
  47.  *
  48.  *    <string>        becomes just  string (recursively)
  49.  *    host!(host!)*@route    becomes a pure !-route
  50.  *
  51.  * NOTE:  We don't handle @route:host!(host!)*@route, for now.  Maybe later.
  52.  *
  53.  * input:
  54.  *    address    - address to be preparsed
  55.  *    error    - error message
  56.  *
  57.  * output:
  58.  *    parsed address, or NULL for parsing error, message returned in error
  59.  *    output is guarranteed to not be a pointer to the input
  60.  */
  61. char *
  62. preparse_address(address, error)
  63.     char *address;            /* address to be preparsed */
  64.     char **error;            /* return error message here */
  65. {
  66.     register char *ap;            /* temp for scanning address */
  67.     char *mark_start = NULL;        /* marked position of < */
  68.     char *mark_end = NULL;        /* marked position of > */
  69.     int nest_cnt = 0;            /* nesting count for angle brackets */
  70.  
  71.     DEBUG1(DBG_ADDR_HI, "preparse_address(%s) entry:\n", address);
  72.     /*
  73.      * scan for < and > pairs and find the last or innermost matching
  74.      * pair.
  75.      */
  76.     for (ap = address; ap && *ap; ap = address_token(ap)) {
  77.     if (*ap == '<') {
  78.         nest_cnt++;
  79.         mark_start = ap + 1;
  80.         mark_end = NULL;
  81.     } else if (*ap == '>') {
  82.         nest_cnt--;
  83.         if (mark_end == NULL) {
  84.         mark_end = ap;
  85.         }
  86.     }
  87.     }
  88.     if (ap == NULL) {
  89.     *error = "bad address token";
  90.     DEBUG1(DBG_ADDR_LO,
  91.            "preparse_address found error %s: returns (null)\n",
  92.            *error);
  93.     return NULL;
  94.     }
  95.     if (mark_start && mark_end == NULL) {
  96.     /* hmm, no match for the < token */
  97.     *error = "no match for `<' in address";
  98.     DEBUG1(DBG_ADDR_LO,
  99.            "preparse_address found error %s: returns (null)\n",
  100.            *error);
  101.     return NULL;
  102.     }
  103.     if (nest_cnt != 0) {
  104.     if (nest_cnt < 0) {
  105.         *error = "no match for > in address";
  106.     } else {
  107.         *error = "no match for < in address";
  108.     }
  109.     DEBUG1(DBG_ADDR_LO,
  110.            "preparse_address found error %s: returns (null)\n",
  111.            *error);
  112.     return NULL;
  113.     }
  114.     /* narrow to the route-addr */
  115.     if (mark_end) {
  116.     *mark_end = '\0';
  117.     address = ap = mark_start;
  118.     }
  119.  
  120.     /*
  121.      * now search for the mutant form: path!@route-addr
  122.      */
  123.     if (*ap == '@') {
  124.     /* valid route-addr, not a mutant one */
  125.     ap = xmalloc((unsigned)(strlen(address) + 1));
  126.     (void) strcpy(ap, address);
  127.     if (mark_end) {
  128.         *mark_end = '>';        /* widden the original address */
  129.     }
  130.     DEBUG1(DBG_ADDR_HI, "preparse_address returns: %s\n", ap);
  131.     return ap;            /*  no transformations */
  132.     }
  133.  
  134.     while (*ap) {
  135.     ap = address_token(ap);
  136.     if (ap == NULL) {
  137.         *error = "bad address token";
  138.         DEBUG1(DBG_ADDR_LO,
  139.            "preparse_address found error %s: returns (null)\n",
  140.            *error);
  141.         return NULL;
  142.     }
  143.     if (*ap != '!') {
  144.         ap = xmalloc((unsigned)(strlen(address) + 1));
  145.         (void) strcpy(ap, address);
  146.         if (mark_end) {
  147.         *mark_end = '>';    /* widden the original address */
  148.         }
  149.         DEBUG1(DBG_ADDR_HI, "preparse address returns: %s\n", ap);
  150.         return ap;        /* address should be okay */
  151.     }
  152.     ap++;
  153.     if (*ap == '@') {
  154.         /* matched host!(host!)*@route -- build the !-route */
  155.         register char *p = xmalloc((unsigned)strlen(address));
  156.         DEBUG(DBG_ADDR_MID, "found host!(host!)*@route form--ugh!\n");
  157.         /* first part already !-route */
  158.         (void) strncpy(p, address, ap-address);
  159.         if (mark_end) {
  160.         *mark_end = '>';    /* widden the original address */
  161.         }
  162.         ap = build_uucp_route(ap, error); /* build !-route */
  163.         if (ap == NULL) {
  164.         DEBUG(DBG_ADDR_MID, "preparse_address returns: (null)\n");
  165.         return NULL;
  166.         }
  167.         (void) strcat(p, ap);    /* concatenate together */
  168.         xfree(ap);
  169.         DEBUG1(DBG_ADDR_HI, "preparse_address returns: %s\n", p);
  170.         return p;            /* transformed */
  171.     }
  172.     }
  173.     ap = xmalloc((unsigned)(strlen(address) + 1));
  174.     (void) strcpy(ap, address);
  175.     if (mark_end) {
  176.     *mark_end = '>';    /* widden the original address */
  177.     }
  178.     DEBUG1(DBG_ADDR_HI, "preparse address returns: %s\n", ap);
  179.     return ap;                /* no transformations */
  180. }
  181.  
  182.  
  183. /*
  184.  * parse_address - extract a target and remainder from an address
  185.  *
  186.  * using the rules in section 3.2 of the mailer.design document,
  187.  * extract a target and a remainder from an address.
  188.  *
  189.  * The target is defined as the first destination host in an address,
  190.  * the remainder is defined as the remaining parat of the address
  191.  * after extracting the target.
  192.  *
  193.  * A short form of the rules for extraction is the following table
  194.  * of addressing forms in order of lowest to highest precedence:
  195.  *
  196.  *    +---------------------------------------------------------------+
  197.  *    | form            | description        | return    |
  198.  *    |-----------------------|-----------------------|---------------|
  199.  *    | @target,remainder    | route from route-addr    | RFC_ROUTE    |
  200.  *    | @target:remainder    | route from route-addr    | RFC_ENDROUTE    |
  201.  *    | remainder@target    | standard mailbox    | MAILBOX    |
  202.  *    | target!remainder    | UUCP !-route        | UUCP_ROUTE    |
  203.  *    | target::remainder    | decnet route        | DECNET    |
  204.  *    | target:remainder    | obsolete berkenet    | BERKNET    |
  205.  *    | remainder%target    | obsolete mailbox hack    | PCT_MAILBOX    |
  206.  *    | remainder        | local address form    | LOCAL        |
  207.  *    +---------------------------------------------------------------+
  208.  *
  209.  * The precedence of the % and ! operators can be switched for
  210.  * addresses of the form a!b%c@d.  This switch will happen if the
  211.  * variable switch_percent_and_bang is TRUE.
  212.  *
  213.  * inputs:
  214.  *    address    - string containing the address to be parsed
  215.  *    target    - where to store pointer to computed target
  216.  *    remainder - where to store pointer to computed target
  217.  *
  218.  * outut:
  219.  *    return the address form as described in the above table.  Also,
  220.  *    return in target a pointer to to the target and return in
  221.  *    remainder a pointer to the remainder.  If an error is detected
  222.  *    return FAIL and load the remainder with an error message.
  223.  *    If target is NULL, then only a form is returned, a target and
  224.  *    remainder are not returned, though an error message may still
  225.  *    be loaded into remainder.
  226.  *
  227.  * in-out:
  228.  *    *flagp - flagp is used to maintain state between invocations
  229.  *         of parse_address() that are used to parse successive
  230.  *         remainder components.  It is used to manage the
  231.  *         variant rules used for RFC1123 compliance for the %
  232.  *         operator in the presense of a user@host address.
  233.  *
  234.  *         When parse_address() is called to parse a complete
  235.  *         address, *flagp should be 0.  If parse_address is
  236.  *         used (perhaps successively) to parse generated
  237.  *         remainder strings, then the previous *flagp value should
  238.  *         be re-passed.  FOUND_MAILBOX will be or'd into *flagp
  239.  *         if a user@host form is encountered, in which case further
  240.  *         parses of remainder addresses may use the RFC1123
  241.  *         precedence interpretation of the % operator.
  242.  *
  243.  * NOTE:  address will be overwritten unless it is in local form, or
  244.  *      a target and remainder are not returned.
  245.  *
  246.  * calls: address_token, back_address_token
  247.  * called by: build_uucp_route
  248.  */
  249. int
  250. parse_address(address, target, remainder, flagp)
  251.     char *address;            /* address to parse (destructively) */
  252.     char **target;            /* store pointer to target host here */
  253.     char **remainder;            /* store pointer to remainder here */
  254.     int *flagp;                /* flag passed between invocations */
  255. {
  256.     char *ep;                /* pointer to end of address */
  257.     register char *last_tokens;        /* start of second to last token */
  258.     register char *ap;            /* pointer for scanning address */
  259.     register char *p;            /* temp */
  260.     int switch_flag;
  261.  
  262.     DEBUG1(DBG_ADDR_HI, "parse_address called: address=%s\n", address);
  263.     /*
  264.      * make sure we have an address
  265.      */
  266.     ap = address;
  267.     if (*ap == '\0') {
  268.     /* nothing to do with a zero-length address */
  269.     *remainder = "null address";
  270.     DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
  271.     return FAIL;
  272.     }
  273.  
  274.     switch_flag = flagp && *flagp & FOUND_MAILBOX && switch_percent_and_bang;
  275.  
  276.     /*
  277.      * does the address begin with @target[,:] ?
  278.      */
  279.     if (*ap == '@') {
  280.     if (target) {
  281.         *target = ap + 1;            /* mark the target */
  282.     }
  283.     ap = address_token(ap + 1);        /* skip target */
  284.     if (ap == NULL) {
  285.         *remainder = "bad address token";
  286.         DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
  287.         return FAIL;
  288.     }
  289.  
  290.     /* ensure that the `,' or `:' is in the address */
  291.     if (!ap) {
  292.         /* interesting, address just contained '@' */
  293.         *remainder = "syntax error:  no target host";
  294.         DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
  295.         return FAIL;
  296.     }
  297.     if (*ap == ',' || *ap == ':') {
  298.         int retval = (*ap==','? RFC_ROUTE: RFC_ENDROUTE);
  299.         if (target) {
  300.         *ap++ = '\0';        /* null terminate target */
  301.         *remainder = ap;
  302.         if (check_target_and_remainder(target, remainder) == FAIL) {
  303.             return FAIL;
  304.         }
  305.         DEBUG2(DBG_ADDR_HI,
  306.                "parse_address: RFC_ROUTE: target=%s, remainder=%s\n",
  307.                *target, *remainder);
  308.         }
  309.         return retval;
  310.     }
  311.     /* we have a syntax error, missing , or : */
  312.     *remainder = "syntax error: , or : missing in route-addr";
  313.     DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
  314.     return FAIL;
  315.     }
  316.  
  317.     /*
  318.      * is the address a standard mailbox ?
  319.      * i.e., does the address end in @target ?
  320.      */
  321.     ep = address + strlen(address);
  322.     last_tokens = back_address_token(ap, ep);
  323.     if (last_tokens && last_tokens > ap) {
  324.     last_tokens = back_address_token(ap, last_tokens);
  325.     }
  326.     if (last_tokens == NULL) {
  327.     *remainder = "bad address token";
  328.     DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
  329.     return FAIL;
  330.     }
  331.     if (last_tokens > ap && *last_tokens == '@') {
  332.     /*
  333.      * it matches @token, null terminate the remainder and finish up;
  334.      * also set FOUND_MAILBOX to turn on RFC1123-compliant parsing
  335.      * of %
  336.      */
  337.     if (flagp)
  338.         *flagp |= FOUND_MAILBOX;
  339.     if (target) {
  340.         *last_tokens = '\0';
  341.         *target = last_tokens+1;
  342.         *remainder = ap;
  343.         if (check_target_and_remainder(target, remainder) == FAIL) {
  344.         return FAIL;
  345.         }
  346.         DEBUG2(DBG_ADDR_HI,
  347.            "parse_address: MAILBOX: target=%s, remainder=%s\n",
  348.            *target, *remainder);
  349.     }
  350.     return MAILBOX;
  351.     }
  352.  
  353.     /*
  354.      * HACK!!  goto percent processing if we are using RFC1123-compliant
  355.      * % parsing
  356.      */
  357.  
  358.     if (switch_flag)
  359.     goto switch_order_percent;
  360.  
  361.  switch_order_bang:
  362.     /*
  363.      * is the address a UUCP !-route ?
  364.      * i.e., does the address begin with target! ?
  365.      */
  366.     p = address_token(ap);
  367.     if (p && *p == '!') {
  368.     /* it matches target!, null terminate target and finish up */
  369.     if (target) {
  370.         *p = '\0';
  371.         *target = ap;
  372.         *remainder = p+1;
  373.         if (check_target_and_remainder(target, remainder) == FAIL) {
  374.         return FAIL;
  375.         }
  376.         DEBUG2(DBG_ADDR_HI,
  377.            "parse_address: UUCP_ROUTE: target=%s, remainder=%s\n",
  378.            *target, *remainder);
  379.     }
  380.     return UUCP_ROUTE;
  381.     }
  382.  
  383.     /*
  384.      * is the address a BERKENET or DECNET syntax?
  385.      */
  386.     if (p && *p == ':') {
  387.     if (*(p + 1) == ':') {
  388.         /* DECNET syntax */
  389.         if (target) {
  390.         *p = '\0';
  391.         *target = ap;
  392.         *remainder = p + 2;
  393.         if (check_target_and_remainder(target, remainder) == FAIL) {
  394.             return FAIL;
  395.         }
  396.         DEBUG2(DBG_ADDR_HI,
  397.                "parse_address: DECNET: target=%s, remainder=%s\n",
  398.                *target, *remainder);
  399.         }
  400.         return DECNET;
  401.     }
  402.     /* Berkenet syntax */
  403.     if (target) {
  404.         *p = '\0';
  405.         *target = ap;
  406.         *remainder = p + 1;
  407.         if (check_target_and_remainder(target, remainder) == FAIL) {
  408.         return FAIL;
  409.         }
  410.         DEBUG2(DBG_ADDR_HI,
  411.            "parse_address: BERKENET: target=%s, remainder=%s\n",
  412.            *target, *remainder);
  413.     }
  414.     return BERKENET;
  415.     }
  416.  
  417.     if (switch_flag)
  418.     goto switch_order_local;
  419.  
  420.  switch_order_percent:
  421.     /*
  422.      * is the address a non-standard mailbox ?
  423.      * i.e., does the address end in %target ?
  424.      */
  425.     if (last_tokens && last_tokens - ap > 0 && *last_tokens == '%') {
  426.     /* it matches @target, null terminate the remainder and finish up */
  427.     if (target) {
  428.         *last_tokens = '\0';
  429.         *target = last_tokens+1;
  430.         *remainder = ap;
  431.         if (check_target_and_remainder(target, remainder) == FAIL) {
  432.         return FAIL;
  433.         }
  434.         DEBUG2(DBG_ADDR_HI,
  435.            "parse_address: PCT_MAILBOX: target=%s, remainder=%s\n",
  436.            *target, *remainder);
  437.     }
  438.     return PCT_MAILBOX;
  439.     }
  440.  
  441.     if (switch_flag)
  442.     goto switch_order_bang;
  443.  
  444.  switch_order_local:
  445.     /*
  446.      * we have a local form address
  447.      */
  448.     if (target) {
  449.     *target = NULL;
  450.     *remainder = ap;
  451.     DEBUG1(DBG_ADDR_HI, "parse_address: LOCAL: remainder=%s\n",
  452.            *remainder);
  453.     }
  454.     return LOCAL;
  455. }
  456.  
  457. /* check_target_and_remainder - check for glaring problems */
  458. static int
  459. check_target_and_remainder(target, remainder)
  460.     char **target;
  461.     char **remainder;
  462. {
  463.     register int c;
  464.     register char *p;
  465.  
  466.     if ((*remainder)[0] == '\0') {
  467.     *remainder = "no remainder address";
  468.     DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
  469.     return FAIL;
  470.     }
  471.     /* the set of chars in the target should be limited to a small set */
  472.     p = *target;
  473.     if (*p == '-') {
  474.     *remainder = "target cannot begin with `-'";
  475.     DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
  476.     return FAIL;
  477.     }
  478.     if (*p == '[') {
  479.     return SUCCEED;
  480.     }
  481.     while (c = *p++) {
  482.     if ( !(isalnum(c) || c == '.' || c == '-' || c == '_' ||
  483.            c == '+' || c == '=') )
  484.     {
  485.         *remainder = "illegal character in hostname";
  486.         DEBUG1(DBG_ADDR_MID, "parse_address: %s\n", *remainder);
  487.         return FAIL;
  488.     }
  489.     }
  490.     return SUCCEED;
  491. }
  492.  
  493.  
  494.  
  495. /*
  496.  * mixed_address - check for mixed operators in an address
  497.  *
  498.  * Return TRUE if the given address contains both a % operator and
  499.  * some set of !-like operators (i.e., !, :, or ::); otherwise,
  500.  * return FALSE.
  501.  */
  502. int
  503. mixed_address(address)
  504.     char *address;
  505. {
  506.     int fndpct = 0;
  507.     int fndbang = 0;
  508.     char *p;
  509.  
  510.     for (p = address; p; p = address_token(p)) {
  511.     switch (*p) {
  512.     case ':':
  513.     case '!':
  514.         if (fndpct)
  515.         return TRUE;
  516.         fndbang = TRUE;
  517.         break;
  518.  
  519.     case '%':
  520.         if (fndbang)
  521.         return TRUE;
  522.         fndpct = TRUE;
  523.         break;
  524.     }
  525.     }
  526.  
  527.     return FALSE;
  528. }
  529.  
  530. /*
  531.  * build_uucp_route - convert an address into a UUCP route.
  532.  *
  533.  * Given an address using any of the addressing forms known to the
  534.  * parse_address() routine, convert that address into a pure uucp
  535.  * !-route.  The return value is always freeable with xfree().
  536.  *
  537.  * If there is an error, return NULL.
  538.  *
  539.  * inputs:
  540.  *    address    - the address to transform into a UUCP !-route
  541.  *    error    - on error, set this to error message, if non-NULL
  542.  *
  543.  * output:
  544.  *    transformed address, or NULL if a syntax error occured
  545.  */
  546. char *
  547. build_uucp_route(address, error, flag)
  548.     char *address;            /* address to transform into !-route */
  549.     char **error;            /* return an error message here */
  550.     int flag;                /* flag returned by parse_address() */
  551. {
  552.     return internal_build_uucp_route(address, error, FALSE, flag);
  553. }
  554.  
  555. /*
  556.  * build_partial_uucp_route - convert an address into a partial UUCP route.
  557.  *
  558.  * Given an address using any of the addressing forms known to the
  559.  * parse_address routine, convert that address into a uucp !-route,
  560.  * possibly with %-forms left at the end.  The return value is always
  561.  * freeable with xfree().
  562.  *
  563.  * If there is an error, return NULL.
  564.  *
  565.  * inputs:
  566.  *    address    - the address to transform into a UUCP !-route
  567.  *    error    - on error, set this to error message, if non-NULL
  568.  *
  569.  * output:
  570.  *    transformed address, or NULL if a syntax error occured
  571.  */
  572. char *
  573. build_partial_uucp_route(address, error, flag)
  574.     char *address;            /* address to transform into !-route */
  575.     char **error;            /* return an error message here */
  576.     int flag;                /* flag from parse_address() */
  577. {
  578.     return internal_build_uucp_route(address, error, TRUE, flag);
  579. }
  580.  
  581. /*
  582.  * internal_build_uucp_route - internal form for uucp-route building
  583.  *
  584.  * called from build_uucp_route and build_partial_uucp_route.  If the
  585.  * `partial' flag is TRUE then the latter style is used, otherwise a
  586.  * pure !-route is built.
  587.  */
  588. static char *
  589. internal_build_uucp_route(address, error, partial, flag)
  590.     char *address;            /* address to transform into !-route */
  591.     char **error;            /* return an error message here */
  592.     int partial;            /* TRUE to allow %-form in route */
  593.     int flag;
  594. {
  595.     struct str str;
  596.     register struct str *sp = &str;    /* dynamic string region */
  597.     int uucp_route = TRUE;        /* TRUE if already pure !-route */
  598.     char *target;            /* target returned by parse_address */
  599.     char *remainder;            /* remainder from parse_address */
  600.     char *storage;            /* malloc region for old address */
  601.  
  602.     DEBUG1(DBG_ADDR_HI, "internal_build_uucp_route entry: address=%s\n",
  603.        address);
  604.     /*
  605.      * allocate a new copy of the address so it can be examined destructively.
  606.      */
  607.     storage = remainder = xmalloc((unsigned)(strlen(address) + 1));
  608.     (void)strcpy(storage, address);
  609.  
  610.     /* initialize for copy into string region */
  611.     STR_INIT(sp);
  612.  
  613.     /* loop until we have a local form or a %-form an error occurs */
  614.     for (;;) {
  615.     int form = parse_address(remainder, &target, &remainder, &flag);
  616.  
  617.     switch (form) {
  618.  
  619.     case FAIL:            /* something went wrong, somewhere */
  620.         *error = remainder;
  621.         DEBUG(DBG_ADDR_MID, "internal_build_uucp_route returns: (null)\n")
  622.         return NULL;
  623.  
  624.     case UUCP_ROUTE:        /* okay, this part is a !-route */
  625.         STR_CAT(sp, target);    /* add target! to route */
  626.         STR_NEXT(sp, '!');
  627.         break;
  628.  
  629.     case PCT_MAILBOX:        /* matched something%host... */
  630.         /*
  631.          * If we are building a pure uucp route, then a%b is just
  632.          * another remote form.  Otherwise, finding this form ends
  633.          * the parsing process.
  634.          */
  635.         if (!partial) {
  636.         goto remote_form;
  637.         }
  638.         /* FALL THROUGH */
  639.  
  640.     case LOCAL:            /* local form, we are done */
  641.         /* if address was already a pure !-route, return the old one */
  642.         if (uucp_route) {
  643.         /* free garbage */
  644.         xfree(storage);
  645.         STR_FREE(sp);
  646.         DEBUG1(DBG_ADDR_HI,
  647.               "internal_build_uucp_route returns: %s (unchanged)\n",
  648.               address);
  649.         return COPY_STRING(address);
  650.         } else {
  651.         /* append final local-part */
  652.         STR_CAT(sp, remainder);
  653.         if (form == PCT_MAILBOX) {
  654.             /* %-form requires the target to be included */
  655.             STR_NEXT(sp, '%');
  656.             STR_CAT(sp, target);
  657.         }
  658.         STR_NEXT(sp, '\0');
  659.         xfree(storage);        /* free garbage */
  660.         STR_DONE(sp);
  661.         DEBUG1(DBG_ADDR_HI, "internal_build_uucp_route returns: %s\n",
  662.                sp->p);
  663.         return sp->p;        /* return completed !-route */
  664.         }
  665.         /*NOTREACHED*/
  666.  
  667.     default:            /* not pure !-route, other form */
  668.     remote_form:
  669.         STR_CAT(sp, target);    /* add target! to route */
  670.         STR_NEXT(sp, '!');
  671.         uucp_route = FALSE;
  672.     }
  673.     }
  674. }
  675.  
  676. /*
  677.  * split_addr_list - split command arguments containing addresses
  678.  *
  679.  * Addresses specified in arguments on the command line should be
  680.  * split on commas, never on spaces.  In addition, the group list
  681.  * convention is not used, so that decnet and berkenet addresses can
  682.  * be entered without requiring quoting.
  683.  */
  684. void
  685. split_addr_list(arg, app)
  686.     char *arg;
  687.     struct addr **app;
  688. {
  689.     struct addr *new;
  690.     char *p, *q;
  691.     int c;
  692.     int level;
  693.  
  694.     /*
  695.      * remove extra white space and comments from the address
  696.      */
  697.  
  698.     arg = COPY_STRING(arg);
  699.  
  700.     strip_rfc822_comments(arg);
  701.  
  702.     p = arg;
  703.     while (c = *p) {
  704.  
  705.     /*
  706.      * rule 1: if the address begins with @, then it is a
  707.      * route-addr, so it should be left alone.
  708.      */
  709.  
  710.     if (c == '@') {
  711.         new = alloc_addr();
  712.         new->in_addr = p;
  713.         new->uid = nobody_uid;
  714.         new->gid = nobody_gid;
  715.         new->succ = *app;
  716.         *app = new;
  717.  
  718.         return;
  719.     }
  720.  
  721.     /*
  722.      * rule 2: if the address begins with <, then search for the
  723.      * ending >, then a comma.  If an error is encountered, leave
  724.      * the address alone.
  725.      */
  726.     q = p;
  727.     if (c == '<') {
  728.         level = 1;
  729.         q = p;
  730.         for (;;) {
  731.         q = address_token(q);
  732.         if (q == NULL)
  733.             break;
  734.         if (*q == '>') {
  735.             --level;
  736.             if (level == 0)
  737.             break;
  738.         }
  739.         }
  740.         if (q == NULL) {
  741.         new = alloc_addr();
  742.         new->in_addr = p;
  743.         new->uid = nobody_uid;
  744.         new->gid = nobody_gid;
  745.         new->succ = *app;
  746.         *app = new;
  747.         break;
  748.         }
  749.         /* FALL THROUGH */
  750.     }
  751.     for (;;) {
  752.         q = address_token(q);
  753.         if (q == NULL)
  754.         break;
  755.         if (*q == ',')
  756.         break;
  757.     }
  758.     if (q) {
  759.         *q++ = '\0';
  760.     }
  761.     new = alloc_addr();
  762.     new->in_addr = p;
  763.     new->uid = nobody_uid;
  764.     new->gid = nobody_gid;
  765.     new->succ = *app;
  766.     *app = new;
  767.     if (q)
  768.         p = q;
  769.     else
  770.         break;
  771.     }
  772.  
  773.     return;
  774. }
  775.  
  776. /*
  777.  * strip_rfc822_comments - strip RFC822 comments and white space form a string
  778.  */
  779.  
  780. void
  781. strip_rfc822_comments(s)
  782.     char *s;
  783. {
  784.     char *p, *q;
  785.     int c;
  786.     int level;
  787.     static char delims[] = "@:;<>.,";
  788.     int space = 0;
  789.  
  790.     p = q = s;
  791.     while (c = *p++) {
  792.     if (isspace(c)) {
  793.         space = 1;
  794.         continue;
  795.     }
  796.     if (c == '(') {
  797.         level = 1;
  798.  
  799.         while (c = *p) {
  800.         p++;
  801.         if (c == '(') {
  802.             level++;
  803.             continue;
  804.         }
  805.         if (c == ')') {
  806.             --level;
  807.             if (level == 0)
  808.             break;
  809.             continue;
  810.         }
  811.         if (c == '\\') {
  812.             if (*p)
  813.             p++;
  814.         }
  815.         }
  816.         continue;
  817.     }
  818.     if (space) {
  819.         space = 0;
  820.         if (q > s && !strchr(delims, *(q - 1)) && !strchr(delims, c)) {
  821.         *q++ = ' ';
  822.         }
  823.     }
  824.     if (c == '\\') {
  825.         *q++ = c;
  826.         if (c = *p) {
  827.         *q++ = c;
  828.         p++;
  829.         }
  830.         continue;
  831.     }
  832.     if (c == '"') {
  833.         *q++ = c;
  834.         while (c = *p) {
  835.         p++;
  836.         *q++ = c;
  837.         if (c == '"')
  838.             break;
  839.         if (c == '\\') {
  840.             if (c = *p) {
  841.             *q++ = c;
  842.             p++;
  843.             }
  844.         }
  845.         }
  846.         continue;
  847.     }
  848.     *q++ = c;
  849.     }
  850.     *q++ = '\0';
  851. }
  852.  
  853.  
  854. /*
  855.  * address_token - scan forward one token in an address
  856.  *
  857.  * an address token is delimited by a character from the set [@!%:,]
  858.  * a token can also be a domain literal between [ and ], or
  859.  * a quoted literal between double quotes.  \ can precede a character
  860.  * to take away its special properties.
  861.  * domain literals and quoted literals and other tokens can be strung
  862.  * together into one single token if they are separated by `.'.  Otherwise
  863.  * a domain literal or quoted literal represents one token.
  864.  *
  865.  * input:
  866.  *    ap    - pointer to start of a token
  867.  *
  868.  * output:
  869.  *    the end of the input token.  Return NULL on error.
  870.  *
  871.  * called by: parse_address
  872.  */
  873. char *
  874. address_token(ap)
  875.     register char *ap;            /* address to be scanned */
  876. {
  877.     static enum state {            /* states for the state machine */
  878.     s_normal,            /* not in a literal or \ escape */
  879.     s_cquote,            /* previous char was \ */
  880.     s_quote,            /* scanning quoted literal */
  881.     s_domlit,            /* scanning domain literal */
  882.     } state;
  883.     enum state save_state;        /* previous state for \ escape */
  884.     int dot = FALSE;            /* TRUE if last char was unescaped . */
  885.  
  886.     /* setup initial state */
  887.     switch (*ap++) {
  888.     case '\0':                /* no tokens */
  889.     return NULL;            /* error */
  890.  
  891.     case '@':                /* delimiters are one token a piece */
  892.     case '!':
  893.     case '%':
  894.     case ':':
  895.     case ',':
  896.     case '>':
  897.     case '<':
  898.     return ap;            /* so return that single token */
  899.  
  900.     case '"':                /* start in a quoted literal */
  901.     state = s_quote;
  902.     break;
  903.  
  904.     case '[':                /* start in a domain literal */
  905.     state = s_domlit;
  906.     break;
  907.  
  908.     case '.':                /* start with an initial dot */
  909.     state = s_normal;
  910.     dot = TRUE;
  911.     break;
  912.  
  913.     case '\\':                /* start initially with \ escape */
  914.     save_state = s_normal;
  915.     state = s_cquote;
  916.     break;
  917.  
  918.     default:                /* otherwise begin in normal state */
  919.     state = s_normal;
  920.     break;
  921.     }
  922.  
  923.     /*
  924.      * scan until end of token
  925.      */
  926.     while (*ap) {
  927.     switch (state) {
  928.  
  929.     case s_normal:            /* scan for token delimeter */
  930.         switch (*ap) {
  931.  
  932.         case '\\':            /* \ escape, save state, then cquote */
  933.         save_state = s_normal;
  934.         state = s_cquote;
  935.         break;
  936.  
  937.         case '[':            /* domain continue if last char is . */
  938.         if (dot) {
  939.             state = s_domlit;
  940.         } else {
  941.             return ap;
  942.         }
  943.         break;
  944.  
  945.         case '"':            /* quote continue if last char is . */
  946.         if (dot) {
  947.             state = s_quote;
  948.         } else {
  949.             return ap;
  950.         }
  951.         break;
  952.  
  953.         case '@':
  954.         case '!':
  955.         case '%':
  956.         case ':':
  957.         case ',':
  958.         case '<':
  959.         case '>':
  960.         return ap;        /* found the end of a token */
  961.         }
  962.         /* dot is TRUE if this char was a dot */
  963.         dot = ('.' == *ap++);
  964.         break;
  965.  
  966.     case s_quote:            /* scan for end of a quote */
  967.         if (*ap == '\\') {
  968.         /* \ escape in quote */
  969.         ap++;
  970.         save_state = s_quote;
  971.         state = s_cquote;
  972.         } else if (*ap++ == '"') {
  973.         /* end of quote -- check for . after it */
  974.         if (*ap == '.') {
  975.             /* if exists, continue scanning */
  976.             state = s_normal;
  977.         } else {
  978.             /* otherwise we have a complete token */
  979.             return ap;
  980.         }
  981.         }
  982.         break;
  983.  
  984.     case s_domlit:            /* scan for end of domain literal */
  985.         if (*ap == '\\') {
  986.         /* \ escape in domain literal */
  987.         ap++;
  988.         save_state = s_domlit;
  989.         state = s_cquote;
  990.         } else if (*ap++ == ']') {
  991.         /* end of domain literal -- check for . after it */
  992.         if (*ap == '.') {
  993.             /* if exists, continue scanning */
  994.             state = s_normal;
  995.         } else {
  996.             /* otherwise we have a complete token */
  997.             return ap;
  998.         }
  999.         }
  1000.         break;
  1001.  
  1002.     case s_cquote:            /* process \ escape */
  1003.         ap++;            /* just skip the char */
  1004.         state = save_state;        /* and return to previous state */
  1005.         break;
  1006.     }
  1007.     }
  1008.  
  1009.     /*
  1010.      * fell through -- error if we are not in the normal state
  1011.      */
  1012.     if (state != s_normal) {
  1013.     return NULL;
  1014.     }
  1015.  
  1016.     return ap;                /* all done, return the token */
  1017.  
  1018. }
  1019.  
  1020.  
  1021. /*
  1022.  * back_address_token - scan backward one token in an address
  1023.  *
  1024.  * see the rules in address_token for how to delimit an address token.
  1025.  * This procedure does it going backwards.
  1026.  *
  1027.  * Note:  this routine is more complex than address_token, because
  1028.  *      addresses are intended to be scanned forward.
  1029.  *
  1030.  * inputs:
  1031.  *    ba    - beginning of an address (firewall)
  1032.  *    ap    - pointer to character past end of token
  1033.  *
  1034.  * output:
  1035.  *    return start of token that ap points past.  Return NULL on error.
  1036.  *
  1037.  * called by: parse_address
  1038.  * calls: escaped
  1039.  */
  1040. char *
  1041. back_address_token(ba, ap)
  1042.     register char *ba;            /* beginning of address (firewall) */
  1043.     register char *ap;            /* character past end of token */
  1044. {
  1045.     static enum state {            /* states for the state machine */
  1046.     s_normal,            /* not in a literal */
  1047.     s_quote,            /* scanning quoted literal */
  1048.     s_domlit,            /* scanning domain literal */
  1049.     } state;
  1050.     int dot = FALSE;            /* TRUE if next char is unescaped . */
  1051.     register char *p;            /* temp */
  1052.  
  1053.     /*
  1054.      * trap no tokens
  1055.      */
  1056.     if (ba == ap) {
  1057.     return NULL;
  1058.     }
  1059.  
  1060.     /*
  1061.      * setup initial state
  1062.      */
  1063.     --ap;                /* backup to end of token */
  1064.     if (p = escaped(ba, ap)) {
  1065.     /* if last char is escaped, we are in the normal state */
  1066.     state = s_normal;
  1067.     ap = p;
  1068.     } else {
  1069.     switch (*ap) {
  1070.     case '@':            /* delimiters are one token a piece */
  1071.     case '!':
  1072.     case '%':
  1073.     case ':':
  1074.     case ',':
  1075.     case '>':
  1076.     case '<':
  1077.         return ap;            /* so return that single token */
  1078.  
  1079.     case '"':            /* start in a quoted literal */
  1080.         state = s_quote;
  1081.         break;
  1082.  
  1083.     case ']':            /* start in a domain literal */
  1084.         state = s_domlit;
  1085.         break;
  1086.  
  1087.     case '.':            /* start with an initial dot */
  1088.         state = s_normal;
  1089.         dot = TRUE;
  1090.         break;
  1091.  
  1092.     default:            /* otherwise begin in normal state */
  1093.         state = s_normal;
  1094.         break;
  1095.     }
  1096.     --ap;                /* this char already processed */
  1097.     }
  1098.  
  1099.     /*
  1100.      * scan until beginning of token
  1101.      */
  1102.     while (ap - ba >= 0) {
  1103.     switch (state) {
  1104.  
  1105.     case s_normal:            /* scan for token delimeter */
  1106.         /* trap escaped character */
  1107.         if (p = escaped(ba, ap)) {
  1108.         ap = p;
  1109.         } else {
  1110.         /* not escaped, process it */
  1111.         switch (*ap) {
  1112.  
  1113.         case ']':        /* domain okay if next char is . */
  1114.             if (dot) {
  1115.             state = s_domlit;
  1116.             } else {
  1117.             return ap+1;
  1118.             }
  1119.             break;
  1120.  
  1121.         case '"':        /* quote okay if next char is . */
  1122.             if (dot) {
  1123.             state = s_quote;
  1124.             } else {
  1125.             return ap+1;
  1126.             }
  1127.             break;
  1128.  
  1129.         case '@':
  1130.         case '!':
  1131.         case '%':
  1132.         case ':':
  1133.         case ',':
  1134.         case '>':
  1135.         case '<':
  1136.             return ap+1;    /* found the end of a token */
  1137.         }
  1138.         /* dot is TRUE if this char was a dot */
  1139.         dot = ('.' == *ap--);
  1140.         }
  1141.         break;
  1142.  
  1143.     case s_quote:            /* scan for end of a quote */
  1144.         if (p = escaped(ba, ap)) {
  1145.         /* trap \ escape */
  1146.         ap = p;
  1147.         } else if (*ap-- == '"') {
  1148.         /* end of quote -- check for . before it */
  1149.         if (ap - ba >= 0 && *ap == '.' && !escaped(ba, ap)) {
  1150.             /* if exists, continue scanning */
  1151.             state = s_normal;
  1152.         } else {
  1153.             /* otherwise we have a complete token */
  1154.             return ap+1;
  1155.         }
  1156.         }
  1157.         break;
  1158.  
  1159.     case s_domlit:            /* scan for end of domain literal */
  1160.         if (p = escaped(ba, ap)) {
  1161.         /* trap \ escape */
  1162.         ap = p;
  1163.         } else if (*ap-- == '[') {
  1164.         /* end of domain literal -- check for . before it */
  1165.         if (ap - ba >= 0 && *ap == '.' && !escaped(ba, ap)) {
  1166.             /* if exists, continue scanning */
  1167.             state = s_normal;
  1168.         } else {
  1169.             /* otherwise we have a complete token */
  1170.             return ap+1;
  1171.         }
  1172.         }
  1173.         break;
  1174.     }
  1175.     }
  1176.  
  1177.     /*
  1178.      * fell through -- error if we are not in the normal state
  1179.      */
  1180.     if (state != s_normal) {
  1181.     return NULL;
  1182.     }
  1183.  
  1184.     return ap+1;            /* all done, return the token */
  1185. }
  1186.  
  1187. /*
  1188.  * escaped - determine if a character is \ escaped, scanning backward
  1189.  *
  1190.  * given the beginning of a string and a character positition within
  1191.  * it, determine if that character is \ escaped or not, tracing through
  1192.  * multiple \ chars if necessary.  Basically, if the character position
  1193.  * is preceded by an odd number of \ chars, the current character is
  1194.  * \ escaped.
  1195.  *
  1196.  * inputs:
  1197.  *    ba    - beginning of string
  1198.  *    ap    - character position in string
  1199.  *
  1200.  * output:
  1201.  *    beginning of set of \ chars previous to ap, or NULL if the
  1202.  *    character at ap is not backslash escaped.
  1203.  *
  1204.  * called by: back_address_token
  1205.  */
  1206. static char *
  1207. escaped(ba, ap)
  1208.     register char *ba;            /* beginning of string */
  1209.     register char *ap;            /* character position in string */
  1210. {
  1211.     register unsigned i = 0;        /* count of \ characters */
  1212.  
  1213.     /*
  1214.      * count the number of preceding \ characters, but don't go past
  1215.      * the beginning of the string.
  1216.      */
  1217.     --ap;
  1218.     while (ap - ba >= 0 && *ap == '\\') {
  1219.     i++; --ap;
  1220.     }
  1221.  
  1222.     /* if odd number of \ chars, then backslash escaped */
  1223.     return (i%2==1)? ap: NULL;
  1224. }
  1225.  
  1226.  
  1227. /*
  1228.  * alloc_addr - allocate a struct addr
  1229.  *
  1230.  * NOTE: the caller must setup the addr fields correctly.  This routine
  1231.  *     marks certain fields with improper values, which unless changed,
  1232.  *     will results in other routines doing a panic().
  1233.  */
  1234. struct addr *
  1235. alloc_addr()
  1236. {
  1237.     register struct addr *addr;        /* our new address */
  1238.  
  1239.     /* grab it */
  1240.     addr = (struct addr *)xmalloc(sizeof(*addr));
  1241.  
  1242.     /* preset the proper values */
  1243.     bzero((char *)addr, sizeof(*addr));
  1244.     addr->match_count = -1;
  1245.     addr->uid = BOGUS_USER;        /* the identity is not known yet */
  1246.     addr->gid = BOGUS_GROUP;        /* the identity is not known yet */
  1247.  
  1248.     return addr;
  1249. }
  1250.  
  1251. /*
  1252.  * insert_addr_list - insert a list of addrs into another list
  1253.  *
  1254.  * insert each addr in an input list at the beginning of an output list.
  1255.  * In the process or in some addr flags and (possibly) set next_addr
  1256.  * to an error message.
  1257.  */
  1258. void
  1259. insert_addr_list(in, out, error)
  1260.     register struct addr *in;        /* input list */
  1261.     register struct addr **out;        /* output list */
  1262.     register struct error *error;    /* error structure (if non-NULL) */
  1263. {
  1264.     struct addr *next;
  1265.  
  1266.     DEBUG(DBG_ADDR_MID, "insert_addr_list() called:\n");
  1267. #ifndef NODEBUG
  1268.     if (error) {
  1269.     DEBUG2(DBG_ADDR_MID, "\tERR%ld: %s\n",
  1270.            error->info & ERR_MASK, error->message);
  1271.     }
  1272. #endif    /* NODEBUG */
  1273.     /* loop over all of the input addrs */
  1274.     for (; in; in = next) {
  1275.     next = in->succ;
  1276.  
  1277.     DEBUG1(DBG_ADDR_MID, "\t%s\n", in->in_addr);
  1278.     if (error) {
  1279.         in->error = error;        /* set the error message, if given */
  1280.     }
  1281.     in->succ = *out;
  1282.     *out = in;
  1283.     }
  1284. }
  1285.  
  1286. /*
  1287.  * addr_sort - sort an input list of addrs and return the new sorted list
  1288.  *
  1289.  * calling sequence is:
  1290.  *    sorted_list = addr_sort(input_list, OFFSET(addr, tag_name)
  1291.  *
  1292.  * where tag_name is the (char *) element name in the addr structure to
  1293.  * sort on.
  1294.  */
  1295. static int sort_offset;            /* pass offset to compare function */
  1296. struct addr *
  1297. addr_sort(in, offset)
  1298.     struct addr *in;
  1299.     int offset;
  1300. {
  1301.     struct addr **addrv;        /* array of addresses */
  1302.     register int addrc;            /* count of addresses */
  1303.     register struct addr **addrp;    /* temp addr pointer */
  1304.     register struct addr *a;        /* address list or current address */
  1305.  
  1306.     /* pass offset value to addrcmp() by setting file local variable */
  1307.     sort_offset = offset;
  1308.  
  1309.     /* count the input addresses */
  1310.     addrc = 0;
  1311.     for (a = in; a; a = a->succ) {
  1312.     addrc++;
  1313.     }
  1314.  
  1315.     /* allocate space for an array for that many pointers */
  1316.     addrv = (struct addr **)xmalloc(addrc * sizeof(*addrv));
  1317.  
  1318.     /* build the array from the input list */
  1319.     for (addrp = addrv, a = in; a; a = a->succ) {
  1320.     *addrp++ = a;
  1321.     }
  1322.  
  1323.     /* sort the array */
  1324.     qsort((char *)addrv, addrc, sizeof(*addrv), addrcmp);
  1325.  
  1326.     /*
  1327.      * turn the sorted array into a sorted list
  1328.      * Start from the end of the array so the generated list will start
  1329.      * from the beginning.
  1330.      */
  1331.     for (addrp = addrv + addrc, a = NULL; addrc > 0; --addrc) {
  1332.     (*--addrp)->succ = a;
  1333.     a = *addrp;
  1334.     }
  1335.  
  1336.     return a;
  1337. }
  1338.  
  1339. /*
  1340.  * addrcmp - compare two addr structures based on a field at sort_offset.
  1341.  */
  1342. static int
  1343. addrcmp(a, b)
  1344.     char **a;
  1345.     char **b;
  1346. {
  1347.     return strcmp(*(char **)(*a + sort_offset), *(char **)(*b + sort_offset));
  1348. }
  1349.  
  1350. /*
  1351.  * note_error - create an error structure for inclusion in an addr structure
  1352.  */
  1353. struct error *
  1354. note_error(info, message)
  1355.     long info;
  1356.     char *message;
  1357. {
  1358.     struct error *ret = (struct error *)xmalloc(sizeof(*ret));
  1359.  
  1360.     ret->info = info;
  1361.     ret->message = message;
  1362.  
  1363.     return ret;
  1364. }
  1365.  
  1366.  
  1367. #ifdef STANDALONE
  1368.  
  1369. #include "varargs.h"
  1370.  
  1371. int return_to_sender = FALSE;
  1372. int exitvalue = 0;
  1373. FILE *errfile;
  1374.  
  1375. #ifdef DEBUG_LEVEL
  1376.  int debug = DEBUG_LEVEL;
  1377. #else    /* DEBUG_LEVEL */
  1378.  int debug = 0;
  1379. #endif    /* DEBUG_LEVEL */
  1380.  
  1381. /*
  1382.  * test the above functions by calling parse_address for each
  1383.  * argument given to the program.
  1384.  */
  1385. void
  1386. main(argc, argv)
  1387.     int argc;                /* count of arguments */
  1388.     char **argv;            /* vector of arguments */
  1389. {
  1390.     char *s;                /* temp string */
  1391.     char *addr;                /* preparsed address */
  1392.     char *error;            /* error message */
  1393.     int form;                /* form from parse_address */
  1394.     char *target;            /* target returned by parse_address */
  1395.     char *remainder;            /* remainder from parse_address */
  1396.     int i;
  1397.  
  1398.     errfile = stderr;
  1399.  
  1400.     /*
  1401.      * if first argument is -s then test the split_addr_list function
  1402.      */
  1403.     if (argc > 1 && EQ(argv[1], "-s")) {
  1404.     struct addr *alist;
  1405.  
  1406.     for (i = 2; i < argc; i++) {
  1407.         alist = NULL;
  1408.         split_addr_list(argv[i], &alist);
  1409.         while (alist) {
  1410.         printf("%s\n", alist->in_addr);
  1411.         alist = alist->succ;
  1412.         }
  1413.     }
  1414.     exit(0);
  1415.     }
  1416.  
  1417.     /*
  1418.      * if first argument is a number, change the debug level
  1419.      */
  1420.     if (argc > 1 && isdigit(argv[1][0])) {
  1421.     debug = atoi(*++argv);
  1422.     argc--;
  1423.     }
  1424.  
  1425.     /*
  1426.      * loop over all arguments or read from standard input if none
  1427.      */
  1428.     if (argc > 1) {
  1429.     while (*++argv) {
  1430.         (void)fprintf(stderr, "input:  %s\n", *argv);
  1431.  
  1432.         /* preparse the address to get rid of mutant forms */
  1433.         addr = preparse_address(*argv, &error);
  1434.         if (addr) {
  1435.         (void)fprintf(stderr, "preparse_address: %s\n", addr);
  1436.         } else {
  1437.         (void)fprintf(stderr, "preparse_address: %s\n", error);
  1438.         break;
  1439.         }
  1440.  
  1441.         /* see what build_uucp_route yields */
  1442.         s = build_uucp_route(addr, &error, 0);
  1443.         if (s) {
  1444.         (void)fprintf(stderr, "build_uucp_route: %s\n", s);
  1445.         } else {
  1446.         (void)fprintf(stderr, "build_uucp_route: %s\n", error);
  1447.         }
  1448.  
  1449.         /* see what parse_address yields */
  1450.         form = parse_address(addr, &target, &remainder, (int *)0);
  1451.         if (form == LOCAL) {
  1452.         (void)printf("LOCAL %s\n", remainder);
  1453.         } else if (form == FAIL) {
  1454.         (void)fprintf(stderr, "parse_address: %s\n", remainder);
  1455.         } else {
  1456.         (void)printf("REMOTE %s@%s\n", remainder, target);
  1457.         }
  1458.     }
  1459.     } else {
  1460.     char line[4096];
  1461.  
  1462.     while (gets(line) != NULL) {
  1463.         (void)fprintf(stderr, "input:  %s\n", line);
  1464.  
  1465.         /* preparse the address to get rid of mutant forms */
  1466.         addr = preparse_address(line, &error);
  1467.         if (addr) {
  1468.         (void)fprintf(stderr, "preparse_address: %s\n", addr);
  1469.         } else {
  1470.         (void)fprintf(stderr, "preparse_address: %s\n", error);
  1471.         break;
  1472.         }
  1473.  
  1474.         /* see what build_uucp_route yields */
  1475.         s = build_uucp_route(addr, &error, 0);
  1476.         if (s) {
  1477.         (void)fprintf(stderr, "build_uucp_route: %s\n", s);
  1478.         } else {
  1479.         (void)fprintf(stderr, "build_uucp_route: %s\n", error);
  1480.         }
  1481.  
  1482.         /* see what parse_address yields */
  1483.         form = parse_address(addr, &target, &remainder, (int *)0);
  1484.         if (form == LOCAL) {
  1485.         (void)printf("LOCAL %s\n", remainder);
  1486.         } else if (form == FAIL) {
  1487.         (void)fprintf(stderr, "parse_address: %s\n", remainder);
  1488.         } else {
  1489.         (void)printf("REMOTE %s@%s\n", remainder, target);
  1490.         }
  1491.     }
  1492.     }
  1493.  
  1494.     exit(exitvalue);
  1495. }
  1496.  
  1497. /*
  1498.  * define panic, fatal and write_log here, rather than
  1499.  * using the external routines.  We are testing and just want
  1500.  * the information displayed, not logged.
  1501.  */
  1502. /*VARARGS2*/
  1503. void
  1504. panic(exitcode, fmt, va_alist)
  1505.     int exitcode;            /* call exit(exitcode) */
  1506.     char *fmt;                /* printf(3) format */
  1507.     va_dcl                              /* arguments for printf */
  1508. {
  1509.     va_list ap;
  1510.  
  1511.     va_start(ap);
  1512.     (void)fprintf(stderr, "PANIC(%s): ", exitcode);
  1513.     (void)vfprintf(stderr, fmt, ap);
  1514.     putc('\n', stderr);            /* fatal messages not \n terminated */
  1515.     va_end(ap);
  1516.  
  1517.     return_to_sender = TRUE;
  1518.     exit(exitcode);
  1519. }
  1520.  
  1521. /*VARARGS2*/
  1522. void
  1523. write_log(log, fmt, va_alist)
  1524.     int log;                /* TRUE if to write global log file */
  1525.     char *fmt;                /* printf(3) format */
  1526.     va_dcl                              /* arguments for printf */
  1527. {
  1528.     va_list ap;
  1529.  
  1530.     va_start(ap);
  1531.     (void)vfprintf(stderr, fmt, ap);
  1532.     putc('\n', stderr);
  1533.     va_end(ap);
  1534. }
  1535.  
  1536. #endif    /* STANDALONE */
  1537.